package com.jlect.swebing.ui.client;

import com.jlect.swebing.ui.client.laf.EventListener;
import com.jlect.swebing.ui.client.laf.SystemEventsConfig;
import com.jlect.swebing.util.client.Logger;

/**
 * Default implementation of the {@link com.jlect.swebing.ui.client.FocusManager} interface
 *
 * @author Sergey Kozmin
 * @since 24.11.2007 18:29:08
 */
public class DefaultFocusManager implements FocusManager, EventListener {
    private static Logger logger = Logger.getLogger("DefaultFocusManager");

    private GComponent focusedComponent;

    public void initialize() {
        SystemEventsConfig.getEventProvider().setEventListener(this);
    }

    public void deInitialize() {
        SystemEventsConfig.getEventProvider().setEventListener(null);
    }

    public void requestFocus(GComponent component) {
        setFocusedComponent(component);
    }

    public boolean eventAppeared(GEvent event) {
        boolean notCanceled = true;
        logger.warn("Event [" + event.toString() + "]");
        if (event.getEventType() == GEvent.MOUSE_EVENT) {
            notCanceled = processMouseEvent((MouseEvent) event);
        } else if (event.getEventType() == GEvent.KEY_EVENT) {
            notCanceled = processKeyEvent((KeyEvent) event);
        }
        return notCanceled;
    }

    private boolean processKeyEvent(KeyEvent event) {
        boolean notCanceled = true;
        switch (event.getKeyEventType()) {
            case KeyEvent.KEY_PRESSED:
            case KeyEvent.KEY_RELEASED: {
                GComponent component = event.getSource();
                if (component != null) {
                    component.fireKeyEvent(event);
                } else {
                    logger.warn("GComponent wasn't registered for event [" + event.toString() + "]");
                }
                break;
            }
            case KeyEvent.KEY_TYPED: {
                if (event.getKeyCode() == GEvent.TAB) {
                    if ((event.getModifiers() & GEvent.SHIFT_MASK) != 0) {
                        focusNextComponent();
                    } else {
                        focusPreviousComponent();
                    }
                    notCanceled = false;
                }
                break;
            }
        }
        return notCanceled;
    }

    private boolean processMouseEvent(MouseEvent event) {
        boolean notCanceled = true;
        switch (event.getMouseType()) {
            case MouseEvent.MOUSE_UP:
            case MouseEvent.MOUSE_DOWN:
            case MouseEvent.MOUSE_MOVE:
            case MouseEvent.MOUSE_OUT:
            case MouseEvent.MOUSE_OVER:
            case MouseEvent.MOUSE_WHEEL: {//can't bring focus on new component
                GComponent component = event.getSource();
                if (component != null) {
                    component.fireMouseEvent(event);
                } else {
                    logger.warn("GComponent wasn't registered for event [" + event.toString() + "]");
                }
                break;
            }
            case MouseEvent.CLICK_EVENT: {//can bring focus on new component
                GComponent component = event.getSource();
                if (component != null) {
                    component.fireMouseEvent(event);
                    if (!component.equals(focusedComponent)) {
                        setFocusedComponent(component.getFocusPolicy().getFirstComponent());
                    }
                } else {
                    logger.warn("GComponent wasn't registered for event [" + event.toString() + "]");
                }
                break;
            }
        }
        return notCanceled;
    }

    private void focusPreviousComponent() {
        GComponent current = focusedComponent;
        while (current != null) {
            GComponent beforeComp = current.getFocusPolicy().getComponentBefore(current);
            if (beforeComp != null) {
                setFocusedComponent(beforeComp);
                break;
            }
            //use parent traversal policy
            current = current.getParent();
        }
    }

    private void focusNextComponent() {
        GComponent current = focusedComponent;
        while (current != null) {
            GComponent afterComponent = current.getFocusPolicy().getComponentAfter(current);
            if (afterComponent != null) {
                setFocusedComponent(afterComponent);
                break;
            }
            //use parent traversal policy
            current = current.getParent();
        }
    }

    private void setFocusedComponent(GComponent component) {
        if (!isComponentsEqual(focusedComponent, component)) {
            if (focusedComponent != null) {
                focusedComponent.focusLost();
            }
            if (component != null) {
                component.focusGained();
            }
            focusedComponent = component;
        }
    }

    private boolean isComponentsEqual(GComponent c1, GComponent c2) {
        return c1 == null ? c2 == null : c1.equals(c2);
    }
}
